import "babel-polyfill";
import type from "../type";
import {map, reduce, zip} from "../algorithm/iterator";
import Integer from "./number/integer";

var op_add = (x, y) => x + y;
var op_multiply = (x, y) => x * y;
var op_square = x => x * x;

export var sum = function() {
    return arguments::reduce(type(this) === "Function" ? this : op_add);
};

export var product = function() {
    return arguments::reduce(type(this) === "Function" ? this : op_multiply);
};

export var innerProduct = function(iterator1, iterator2, {add = op_add, multiply = op_multiply} = {}) {
    return add::sum(...zip(iterator1, iterator2)::map(list => multiply(...list)));
};

export var fastExponentation = function(base, exponent, {multiply = op_multiply, square = op_square, one = 1} = {}) {
    exponent = Integer.from(exponent);
    if (exponent.sign < 0) {
        throw new RangeError("Exponent should be non-negative");
    }
    var result = one;
    for (let bit = exponent.bitLength; --bit >= 0;) {
        result = square(result);
        if (exponent.bit(bit)) {
            result = multiply(result, base);
        }
    }
    return result;
};
